package com.hulk.boot.common.util;

import java.util.Random;

/**
 * @author cmt
 * 
 *         This class is used to generate a passwod, it's based on a string
 *         given as input it's possible to define the kind of pwd is needed
 */
public class RandomPwdGenerator {
	static private boolean consonantNext = true;

	// static private long prevWhen = 0;

	/**
	 * 64-bit pool of randomness, maintained by method 'stir' initial value from
	 * MD5 (see stir)
	 */
	static private long ranPool = 0x67452301efcdab89L;
	static private int ntmpl = 0; // keeps track of where we are in the template
	private String pwd = ""; // passwords appear here

	/**
	 * "C" random alphanumeric [0-9,A-Z] "A" random letter upper case "a" random
	 * letter lower case "9" random digit "6" random dice throw "S" random
	 * syllabe "H" random hex digit "*" random special char in the array there
	 * are some template, is possibli to change and add some examle: { "C",
	 * "A","a", "9", "6", "S", "H", "*"}{"CAa96SH*","CAa96SH","AaS*","AaS"}; or
	 * {"AAA 999 HHH CCC 666", "SSS 9 SSS", "CCC CCC CCC",
	 * "HHHH HHHH HHHH HHHH", "AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA",
	 * "99999 99999 99999 99999 99999 99999 99999 99999",
	 * "66666 66666 66666 66666 66666"};
	 */
	private String[] template = { "C", "A", "a", "9", "6", "S", "H", "*" };
	private String templ = null; // the template selected

	public String create(String base, int length, boolean useSpecialChar,
			boolean useNumber, boolean useLowerCase, boolean useUpperCase) {
		if (!useSpecialChar && !useNumber && !useLowerCase && !useUpperCase) {
			return ""; // nothing to do
		}

		Random r = new Random();
		pwd = "";

		findTemplate(useSpecialChar, useNumber, useLowerCase, useUpperCase);

		if (base.length() == 0) {
			// if base is empty use ranPool as base string
			base = "" + ranPool;
		}

		base = base.trim();
		base = base.replace(' ', 'a');

		while (pwd.length() < length) {
			stir(Character.getNumericValue(base
					.charAt(r.nextInt(base.length())))
					+ Character.getNumericValue(base.charAt(r.nextInt(base
							.length()))));
			addChar();
		}

		return pwd.substring(0, length);
	}

	/** Find the desired teplate. */
	private void findTemplate(boolean useSpecialChar, boolean useNumber,
			boolean useLowerCase, boolean useUpperCase) {
		templ = "";

		if (useSpecialChar) {
			templ += template[7];
		}

		if (useNumber) {
			templ += template[3];
			templ += template[4];
		}

		if (useUpperCase) {
			templ += template[1];
		}

		if (useLowerCase) {
			templ += template[2];
			templ += template[5];
		}

		if ((useUpperCase) && (useNumber)) {
			templ += template[6];
			templ += template[0];
		}
	}

	/**
	 * maintain a pool of randomness using a modified 64-bit congruential
	 * generator with multipliers dynamiclly selected from a set of
	 * pseudo-random values.
	 */
	private void stir(long x) {
		// The following 31 constants are the first 62 "magic" numbers
		// from the MD5 algorithm, RFC1321, concatenated in pairs to
		// form 64-bit Java long words. Any random 64-bit values would do,
		// but these were selected from a public source for added user
		// confidence.
		// The stir algorithm itself has nothing to do with MD5.
		// agr 270996
		long[] p = { 0xd76aa478e8c7b756L, // 1,2
				0x242070dbc1bdceeeL, // 3,4
				0xf57c0faf4787c62aL, // 5,6
				0xa8304613fd469501L, // 7,8
				0x698098d88b44f7afL, // 8,10
				0xffff5bb1895cd7beL, // 11,12
				0x6b901122fd987193L, // 13,14
				0xa679438e49b40821L, // 15,16
				0xf61e2562c040b340L, // 17,18
				0x265e5a51e9b6c7aaL, // 19,20
				0xd62f105d02441453L, // 21,22
				0xd8a1e681e7d3fbc8L, // 23,24
				0x21e1cde6c33707d6L, // 25,26
				0xf4d50d87455a14edL, // 27,28
				0xa9e3e905fcefa3f8L, // 29,30
				0x676f02d98d2a4c8aL, // 31,32
				0xfffa39428771f681L, // 33,34
				0x6d9d6122fde5380cL, // 35,36
				0xa4beea444bdecfa9L, // 37,38
				0xf6bb4b60bebfbc70L, // 39,40
				0x289b7ec6eaa127faL, // 41,42
				0xd4ef308504881d05L, // 43,44
				0xd9d4d039e6db99e5L, // 45,46
				0x1fa27cf8c4ac5665L, // 47,48
				0xf4292244432aff97L, // 49,50
				0xab9423a7fc93a039L, // 51,52
				0x655b59c38f0ccc92L, // 53,54
				0xffeff47d85845dd1L, // 55,56
				0x6fa87e4ffe2ce6e0L, // 57,58
				0xa30143144e0811a1L, // 59,60
				0xf7537e82bd3af235L };

		int pIndex;

		pIndex = mod(ranPool, p.length);

		ranPool = (ranPool + x) * p[pIndex];

		pIndex = mod(ranPool, p.length);

		ranPool = ranPool ^ p[pIndex];
	}

	/** x mod y function. Returns positive modulus only. agr */
	private int mod(long x, long y) {
		if (x < 0) {
			x = -x;
		}

		if (y < 0) {
			y = -y;
		}

		return (int) (x % y);
	}

	/** Add a random character to the textArea as specified by the template */
	private void addChar() {
		int ch = 0;
		char tmplChar;
		Character charIn;

		// String templ = template[r.nextInt (template.length)];
		if (ntmpl >= templ.length()) {
			// pwd += "\n";
			ntmpl = 0;

			consonantNext = true;

			return;
		}

		tmplChar = templ.charAt(ntmpl++);

		if (tmplChar == ' ') {
			ch = ' ';
		} else if (tmplChar == 'A') { // random letter upper case
			ch = mod(ranPool, 26) + 'A';
		} else if (tmplChar == 'a') { // random letter lower case
			ch = mod(ranPool, 26) + 'a';
		} else if (tmplChar == 'C') { // random alphanumeric [0-9,A-Z]
			ch = mod(ranPool, 36);

			if (ch < 10) {
				ch = ch + '0';
			} else {
				ch = (ch + 'A') - 10;
			}
		} else if (tmplChar == 'H') { // random hex digit
			ch = mod(ranPool, 16);

			if (ch < 10) {
				ch = ch + '0';
			} else {
				ch = (ch + 'A') - 10;
			}
		} else if (tmplChar == 'S') { // random syllable
			addSyllable();

			return;
		} else if (tmplChar == '*') { // random special Char
			addSpecialChar();

			return;
		} else if (tmplChar == '6') { // random dice throw
			ch = mod(ranPool, 6) + '1';
		} else if (tmplChar == '9') { // random digit
			ch = mod(ranPool, 10) + '0';
		} else {
			return;
		}

		charIn = new Character((char) ch);

		pwd += charIn.toString();

		consonantNext = true;

		ch = 0;

		charIn = null;
	}

	/** Add a random syllable half to the textArea */
	private void addSyllable() {
		String[] consonants = { "b", "c", "d", "f", "g", "h", "j", "k", "l",
				"m",

				"n", "p", "qu", "r", "s", "t", "v", "w", "x", "z",

				"ch", "cr", "fr", "nd", "ng", "nk", "nt", "ph", "pr", "rd",

				"sh", "sl", "sp", "st", "th", "tr" };

		String[] vowels = { "a", "e", "i", "o", "u", "y" };

		String syl = "";

		if (consonantNext) {
			syl = consonants[mod(ranPool, consonants.length)];

			if (syl != "qu") {
				consonantNext = false;
			}
		} else {
			syl = vowels[mod(ranPool, vowels.length)];

			consonantNext = true;
		}

		pwd += syl;

		syl = "";
	}

	/** Add a random syllable half to the textArea */
	private void addSpecialChar() {
		String[] specialChars = { "!", "??", "$", "%", "&", "/", "(", ")", "=",
				"?", "*", "-", "+", ",", ".", ":", ";", "#", "@", "_", "<", ">" };
		String syl = "";

		syl = specialChars[mod(ranPool, specialChars.length)];

		pwd += syl;
	}
}
